#include "ucdinputfile.h"

#include "GL/glut.h"
#include "model.h"
#include <vector>

using namespace visualization;
Model modl;
// This varaible specifys the segment number of the range of the data.
static const int seg = 500;
// Record the number of data point at the corrsponding value.
vector<double> num_of_point;

// The image to draw.
GLubyte _image[seg + 1][seg + 1][3];

// Sometimes the histogram is too low. So it should be magnified.
double magnify;

void setMagnify(double d = 50)
{
	magnify = d;
}
void makeImage(void)
{
	int i, j;//, c;
	// Initializatoin.
	for(i = 0; i <= seg; i++)
	{
		for(j = 0; j <= seg; j++)
		{
			for(int k = 0; k < 3; k++)
			{
				_image[i][j][k] = 180;
			}
		}
	}
}
bool computeGradient(double &component, const double step, const int flag, const std::vector<double> centralPoint)
{
	double forwardP[3], backwardP[3];
	for(int i = 0; i < 3; i++)
	{
		forwardP[i] = backwardP[i] = centralPoint[i];
	}
	std::vector<double> fvalue, bvalue;
	int index;

	switch(flag)
	{
		// X
		case 0:
		{
			index = 0;
			break;
		}
		// Y
		case 1:
		{
			index = 1;
			break;
		}
		// Z
		case 2:
		{
			index = 2;
			break;
		}
	}
	forwardP[index] = centralPoint[index] + step;
	backwardP[index] = centralPoint[index] - step;

	if( !modl.interpolatedValue(forwardP, 3, &fvalue, 10, 10) 
		|| !modl.interpolatedValue(backwardP, 3, &bvalue, 10, 10) )
	{
		return 0;
	}

	component = (fvalue[0] - bvalue[0])/(2*step);

	return 1;
}

void init(void)
{
	// The range of the data.
	double dataLow, dataHigh;
	double step = 1.;

	if(! modl.getDataValueRange(dataLow, dataHigh) )
	{
		exit(-1);
	}

	// Change the dataHight to steepest slope.
	dataHigh = (dataHigh - dataLow) / (step);
	dataLow = 0;

	double interval = (dataHigh - dataLow) / seg;

	// Note that here "i <= seg".
	for(int i = 0; i <= seg; i++)
	{
		num_of_point.push_back(0);
	}

	std::vector<double> coord;
//	double highest = -1;
	double usedP = 0;
	double gx, gy, gz;
	double magnitude;

	for(unsigned int i = 1; i <= modl.getPntNumberOfModel(); i++)
	{
		// For each point, compute the slope, or gradient.
		// Compute index, and then let corresponding num_of_piont add 1.
		modl.getPointCoordinates(i, coord);
		if(   !computeGradient(gx, step, 0, coord)
		   || !computeGradient(gy, step, 1, coord)
		   || !computeGradient(gz, step, 2, coord)
		  )
		{
			continue;
		}
		magnitude = sqrt( pow(gx, 2) + pow(gy, 2) + pow(gz, 2) );
		usedP++;
		int index = magnitude / interval + 0.5;
		num_of_point[index] += 1;
	}
	// Normalize the number to let them fall between 0 and 1.
	double tmp = magnify * seg/( (double)usedP );
	for(unsigned int i = 0; i < num_of_point.size(); i++)
	{
		num_of_point[i] *= tmp;
	}

	glClearColor(1.0, 1.0, 1.0, 0.0);
	glShadeModel(GL_FLAT);
	makeImage();
	// Assign the result to _image.
	for(int j = 0; j <= seg; j++)
	{
		for(int i = 0; i <= num_of_point[j] && i <= seg; i++)
		{
			for(int k = 0; k < 3; k++)
			{
				_image[i][j][k] = 0;
			}
		}
	}
	
	// Draw some scales.
	const int squareLen = 8;
	const int scalePointNum = 5;
	for(int i = 0; i < squareLen; i++)
	{
		for(int j = 0; j <= seg; j += seg/scalePointNum)
		{
			for(int k = j; k < j + squareLen; k++)
			{
				_image[i][k][0] = 0;
				_image[i][k][1] = 100;
				_image[i][k][2] = 200;
			}
		}
	}
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
}
void display(void)
{
	glClear(GL_COLOR_BUFFER_BIT);
	glRasterPos2f(-0.97, -0.97);
	glDrawPixels(seg + 1, seg + 1, GL_RGB, GL_UNSIGNED_BYTE, _image);
	glFlush();
}
void reshape (int w, int h)
{
	glViewport (0, 0, (GLsizei) w, (GLsizei) h);
	glMatrixMode (GL_PROJECTION);
	glLoadIdentity ( );
	if (w <= h)
	{
		glOrtho(-1.5, 1.5, -1.5 * ( GLfloat ) h / ( GLfloat ) w,
				1.5 * ( GLfloat ) h / ( GLfloat ) w, -10.0, 10.0 );
	}
	else
	{
		glOrtho(-1.5 * ( GLfloat ) w / ( GLfloat ) h,
				1.5 * ( GLfloat ) w / ( GLfloat ) h, -1.5, 1.5, -10.0, 10.0);
	}
	glMatrixMode ( GL_MODELVIEW );
	glLoadIdentity ( ) ;
}
void keyboard ( unsigned char key, int x, int y)
{
	switch (key) 
	{
		case 27:
			exit ( 0 );
			break;
	}
}

int main(int argc, char** argv)
{
	if(argc != 2 && argc != 3)
	{
		return 0;
	}
	UCDInputFile file(argv[1]);

	file.FGetModel(modl);
	modl.buildKDTree();
	setMagnify();

	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(800, 800);
	glutInitWindowPosition(100, 100);
	glutCreateWindow(argv[0]);
	init();
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutMainLoop();

	return 0;
}
